import cv2
import numpy as np
import matplotlib.pyplot as plt

def binarize_image(img_path):
    # 读入灰度图
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
    if img is None:
        raise FileNotFoundError("图片路径错误或文件不存在！")

    # 高斯滤波去噪
    blur = cv2.GaussianBlur(img, (5, 5), 0)

    # Otsu 自动阈值二值化
    _, binary = cv2.threshold(blur, 0, 255,
                              cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 目前图里是黑细胞 + 灰底，二值后通常是黑色细胞
    # 我希望细胞为白色，背景为黑色，便于后续处理：所以要反色
    binary = cv2.bitwise_not(binary)

    return img, binary

def analyze_cells(binary, min_area=30):
    """
    输入：二值图（细胞是白色前景）
    输出：
        cell_info: 每个细胞的信息列表（已按圆形度排序）
        result_img: 画了轮廓和编号的结果图
    """
    # 找外部轮廓（每个轮廓对应一个细胞或连通域）
    contours, hierarchy = cv2.findContours(
        binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )

    # 用彩色图来画结果
    result_img = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)

    cell_info = []
    idx = 0
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area < min_area:
            # 太小的区域当成噪声，忽略
            continue

        perimeter = cv2.arcLength(cnt, True)
        if perimeter == 0:
            continue

        # 圆形度 circularity = 4πA / P^2
        circularity = 4 * np.pi * area / (perimeter ** 2)

        # 计算质心，用来标文字
        M = cv2.moments(cnt)
        if M["m00"] != 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
        else:
            cx, cy = cnt[0][0]

        cell_info.append({
            "id": idx,
            "area": area,
            "perimeter": perimeter,
            "circularity": circularity,
            "centroid": (cx, cy),
            "contour": cnt
        })
        idx += 1

    # 按圆形度从大到小排序：越圆越靠前
    cell_info.sort(key=lambda x: x["circularity"], reverse=True)

    # 在图像上画轮廓和编号（按排序后的顺序标 rank）
    for rank, cell in enumerate(cell_info, start=1):
        cnt = cell["contour"]
        cx, cy = cell["centroid"]

        # 画轮廓
        cv2.drawContours(result_img, [cnt], -1, (0, 0, 255), 1)
        # 标注 rank（序号）
        text = str(rank)
        cv2.putText(result_img, text, (cx - 5, cy + 5),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)

    return cell_info, result_img

if __name__ == "__main__":
    img_path = "test_image/test_6/02.jpg"

    # 1. 二值化
    gray, binary = binarize_image(img_path)

    # 2. 连通域分析 + 圆形度排序
    cell_info, result_img = analyze_cells(binary, min_area=30)

    # 细胞数量
    num_cells = len(cell_info)
    print("细胞总数：", num_cells)

    # 打印每个细胞的圆形度信息
    print("按圆形度从大到小排序：")
    for rank, cell in enumerate(cell_info, start=1):
        print(f"Rank {rank:2d}: id={cell['id']}, "
              f"Area={cell['area']:.1f}, "
              f"Perimeter={cell['perimeter']:.1f}, "
              f"Circularity={cell['circularity']:.3f}")

    # 3. 显示结果
    plt.figure(figsize=(10, 4))

    plt.subplot(1, 3, 1)
    plt.imshow(gray, cmap="gray")
    plt.title("Original")
    plt.axis("off")

    plt.subplot(1, 3, 2)
    plt.imshow(binary, cmap="gray")
    plt.title("Binary")
    plt.axis("off")

    plt.subplot(1, 3, 3)
    plt.imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB))
    plt.title(f"Result (cells={num_cells})")
    plt.axis("off")

    plt.tight_layout()
    plt.show()
